//
// Copyright 2010 Cinch Logic Pty Ltd.
//
// http://www.chililog.com
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
package org.chililog.server.engine;
import java.security.Principal;
import java.security.acl.Group;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import javax.security.auth.Subject;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.login.FailedLoginException;
import javax.security.auth.login.LoginException;
import javax.security.auth.spi.LoginModule;
import org.apache.commons.lang.StringUtils;
import org.chililog.server.common.AppProperties;
import org.chililog.server.data.MongoConnection;
import org.chililog.server.data.UserBO;
import org.chililog.server.data.UserController;
import com.mongodb.DB;
/**
* <p>
* JAAS login module for HornetQ. We lookup the user table, validate the password and then add the roles to the user
* </p>
*
* @author vibul
*
*/
public class JAASLoginModule implements LoginModule {
private Subject _subject;
private String _systemUsername;
private String _systemPassword;
/**
* Basic constructor
*/
public JAASLoginModule() {
_systemUsername = AppProperties.getInstance().getMqSystemUsername();
_systemPassword = AppProperties.getInstance().getMqSystemPassword();
return;
}
public boolean abort() throws LoginException {
return true;
}
public boolean commit() throws LoginException {
return true;
}
public void initialize(final Subject subject,
final CallbackHandler callbackHandler,
final Map<String, ?> sharedState,
final Map<String, ?> options) {
_subject = subject;
}
/**
* <p>
* We check the credentials against the repository. By convention, the username is the repository name and the
* password is either the publisher or subscriber password. The role assigned to the user is constructed from the
* combination of username and publisher password.
* </p>
*
* @return Returns true if this method succeeded, or false if this LoginModule should be ignored.
*/
public boolean login() throws LoginException {
try {
//
// This code is from org.hornetq.spi.core.security.JAASSecurityManager.getAuthenticatedSubject();
// It is how HornetQ uses JAAS to authenticate
//
// Subject subject = new Subject();
// if (user != null)
// {
// subject.getPrincipals().add(principal);
// }
// subject.getPrivateCredentials().add(passwordChars);
// LoginContext lc = new LoginContext(configurationName, subject, callbackHandler, config);
// Get the user name
Iterator<Principal> iterator = _subject.getPrincipals().iterator();
String username = iterator.next().getName();
if (StringUtils.isBlank(username)) {
throw new FailedLoginException("Username is requried.");
}
// Get the password
Iterator<char[]> iterator2 = _subject.getPrivateCredentials(char[].class).iterator();
char[] passwordChars = iterator2.next();
String password = new String(passwordChars);
if (StringUtils.isBlank(password)) {
throw new FailedLoginException("Password is requried.");
}
// Check if system user
if (username.equals(_systemUsername) && password.equals(_systemPassword)) {
Group roles = new SimpleGroup("Roles");
roles.addMember(new SimplePrincipal(UserBO.SYSTEM_ADMINISTRATOR_ROLE_NAME));
_subject.getPrincipals().add(roles);
return true;
}
// Let's validate non-system user
DB db = MongoConnection.getInstance().getConnection();
UserBO user = UserController.getInstance().tryGetByUsername(db, username);
if (user == null) {
throw new FailedLoginException("Invalid username or password.");
}
if (StringUtils.isBlank(password) || !user.validatePassword(password)) {
throw new FailedLoginException("Invalid username or password.");
}
// Add role
Group roles = new SimpleGroup("Roles");
for (String role : user.getRoles()) {
roles.addMember(new SimplePrincipal(role));
}
_subject.getPrincipals().add(roles);
// OK
return true;
} catch (Exception ex) {
throw new LoginException(ex.getMessage());
}
}
/**
* There is nothing special to do to log out. We don't have sessions to clear.
*/
public boolean logout() throws LoginException {
return true;
}
/**
* @return Returns the current subject of authentication
*/
public Subject getSubject() {
return _subject;
}
/**
* Group to store a collection of roles expressed as a Principal
*
* @author vibul
*
*/
public class SimpleGroup implements Group {
private final String _name;
private final Set<Principal> _members = new HashSet<Principal>();
public SimpleGroup(final String name) {
this._name = name;
}
public boolean addMember(final Principal principal) {
return _members.add(principal);
}
public boolean isMember(final Principal principal) {
return _members.contains(principal);
}
public Enumeration<? extends Principal> members() {
return Collections.enumeration(_members);
}
public boolean removeMember(final Principal principal) {
return _members.remove(principal);
}
public String getName() {
return _name;
}
}
/**
* Simple principal for storing a role name
*
* @author vibul
*
*/
public static class SimplePrincipal implements Principal, java.io.Serializable {
private static final long serialVersionUID = 1L;
private final String _name;
public SimplePrincipal(final String name) {
this._name = name;
}
/**
* Compare this SimplePrincipal's name against another Principal
*
* @return true if name equals another.getName();
*/
@Override
public boolean equals(final Object another) {
if (!(another instanceof Principal)) {
return false;
}
String anotherName = ((Principal) another).getName();
boolean equals = false;
if (_name == null) {
equals = anotherName == null;
} else {
equals = _name.equals(anotherName);
}
return equals;
}
@Override
public int hashCode() {
return _name == null ? 0 : _name.hashCode();
}
@Override
public String toString() {
return _name;
}
public String getName() {
return _name;
}
}
}